#ifndef JSONCPPPATH_H
#define JSONCPPPATH_H

#include "jsoncpp/include/json/json.h"

#include <QObject>
#include <QVariant>
#include <QSharedPointer>

/**
 * @brief The JsonCppPath class
 * 一个通过类似window路径访问json的方法
 */
class JsonCppPath : public QObject
{
    Q_OBJECT
public:
    JsonCppPath(const QString &jsonpath, QObject *parent = nullptr);
    ~JsonCppPath();

    bool isNullJson();//回合QSharedPointor的isNull重复
    bool isModified();

    /**
     * @brief setValue
     * json的赋值
	 * 支持：uint、int、bool、double、QString、QStringList、QVarintMap
     */
    bool setValue(const QString &path, const QVariant &var);
    bool setValueArg(const QString &path, const QString arg0, const QVariant &var);
    bool setValueArg(const QString &path, int arg0, const QVariant &var);

    bool setJsonValue(const QString &path, Json::Value var, const QString &comment = "", Json::CommentPlacement placement = Json::commentAfterOnSameLine);
    bool setJsonValueArg(const QString &path, const QString &arg0, const Json::Value &var);

    JsonCppPath &append(
            const QString &path, const QVariant &var,
            const QString &comment = "", Json::CommentPlacement placement = Json::commentAfterOnSameLine);
    JsonCppPath &appendJsonValue(
            const QString &path, const Json::Value &var,
            const QString &comment = "", Json::CommentPlacement placement = Json::commentAfterOnSameLine);

    /**
     * @brief value
     * 返回path对应的值，eg: value("/global/node/galaxy/earth/name", "apple");
	 * 支持：uint、int、bool、double、QString、QVariantList、QVarintMap
     */
    QVariant value(const QString &path, const QVariant &defaultValue = QVariant()) const;
    QVariant valueArg(const QString &path, int arg0, const QVariant &defaultValue = QVariant());
    QVariant valueArg(const QString &path, QString arg0, const QVariant &defaultValue = QVariant());

    Json::Value jsonValue(const QString &path, const Json::Value &defaultValue = Json::Value(Json::nullValue));
    Json::Value jsonValueArg(const QString &path, QString arg0, const Json::Value &defaultValue = Json::Value(Json::nullValue));
    Json::Value jsonValueArg(const QString &path, int arg0, const Json::Value &defaultValue = Json::Value(Json::nullValue));

    /**
     * @brief size
     * 返回array的size或object的size
     */
    int size(const QString &path = "");

    bool remove(const QString &path);

    void clear();

    /**
     * @brief keys
     * 返回object的keys
     */
    QStringList keys(const QString &path = "");

    /**
     * @brief setComment
     * 设置注释信息，默认为//comment
     */
    bool setComment(const QString &path, const QString &comment, Json::CommentPlacement placement = Json::commentAfterOnSameLine);
    bool setComment(Json::Value *json, QString comment, Json::CommentPlacement placement = Json::commentAfterOnSameLine);

public:
    QByteArray toJson();
    QByteArray toJson(const QString &path);
    bool saveToFile();
    bool saveToFile(const QString &filepath);

    QString fileName();

    bool parse();
    bool parse(const QByteArray &content);
    QString errorString();

    static QVariant jsonValuetoVariant(const Json::Value &value);
    static Json::Value varianttoJsonValue(const QVariant &value);

    Json::Value *findPath(const QString &path) const;
    Json::Value *makePath(const QString &path);

    bool exists(const QString &path);

    /**
     * @brief breakPath
     * 转换: "/11/22/22" 到 (/11,/22,/33)
     */
    QStringList breakPath(const QString &path) const;

    /**
     * @brief getPathJsonPtr
     * 获取指定路径的JsonCppPath的共享指针
     * @param isMakePath : getPathJsonPtr maybe null if false， sure valid if true
     *
     * 示例：
     * [1]  提供多层遍历json的简单方法，支持object和array，如下
     *      JsonCppSettings r;
     *      for(auto x : r.getChildrenJsonPtr("/xList")
     *          for(auto y : x->getChildrenJsonPtr("/yList")
     *              qDebug() << y.value("/name").toString();
     *
     * [2]  提供修改json的另一种方法
     *      JsonCppSettings rootJson("example.json");
     *      auto xObject = rootJson.getPathJsonPtr("/xObject");
     *      xObject->setValue("/name", "json");
     *      rootJson析构时，会将修改的部分写入"example.json"
     */
    QSharedPointer<JsonCppPath> getPathJsonPtr(const QString &path, bool isMakePath = false);
    QSharedPointer<JsonCppPath> getChildJsonPtr(int k);
    QList<QSharedPointer<JsonCppPath>> getChildrenJsonPtr(const QString &path);

    /**
     * @brief findInChildren
     * 检索指定路径下的child的属性值，并返回child的ptr
     *
     * 示例：
     * [1]  提供修改json的另一种方法
     *      JsonCppSettings rootJson("example.json");
     *      auto xObject = rootJson.findInChildren("/xObject", "/name", "Jerry");
     *      if(!xObject.isNull())
     *          xObject->setValue("/name", "Tom");
     *      rootJson析构时，会将修改的部分写入"example.json"
     * @return
     */
    QSharedPointer<JsonCppPath> findInChildren(const QString &path, const QString &childPath, const QVariant &var);

private:
    Json::Value    *m_rootJson;
    QString         m_errorString;
    QString         m_jsonpath;
    bool            m_isModified = false;//标志：记录json是否被修改过
    bool            m_isAutoDelete = true; //标记析构时是否删除m_rootJson

    friend class JsonCppSettings;
    friend class JsonCppReader;
    friend class JsonCppVoid;
};

class JsonCppSettings : public JsonCppPath{
    Q_OBJECT
public:
    JsonCppSettings(const QString &jsonpath, QObject *parent = nullptr);
    virtual ~JsonCppSettings();
};


class JsonCppReader : public JsonCppPath{
    Q_OBJECT
public:
    JsonCppReader(const Json::Value &json, QObject *parent = nullptr);
    JsonCppReader(const QByteArray &json, QObject *parent = nullptr);
    JsonCppReader(const char *json, QObject *parent = nullptr);
    JsonCppReader(QObject *parent = nullptr);
    virtual ~JsonCppReader();
};

class JsonCppPathReader : public JsonCppPath{
    Q_OBJECT
public:
    JsonCppPathReader(const QString &jsonpath, QObject *parent = nullptr);
    virtual ~JsonCppPathReader();
};


class JsonCppVoid : public JsonCppPath{
    Q_OBJECT
public:
    JsonCppVoid(QObject *parent = nullptr);
    virtual ~JsonCppVoid();
};



#endif // JSONCPPPATH_H
